﻿using Org.BouncyCastle.Utilities.Encoders;
using System.ComponentModel;
using System.Text;

namespace PmSoft.Core.Cryptos;

/// <summary>
/// Sm4 加密算法工具类，对标国际 DES 算法
/// </summary>
public class Sm4CryptoUtility
{
	/// <summary>
	/// 数据（明文）
	/// </summary>
	public string Data { get; set; }

	/// <summary>
	/// 秘钥
	/// </summary>
	public string Key { get; set; }

	/// <summary>
	/// 向量（IV）
	/// </summary>
	public string Iv { get; set; }

	/// <summary>
	/// 明文是否是十六进制
	/// </summary>
	public bool HexString { get; set; }

	/// <summary>
	/// 加密模式（默认使用 CBC 模式）
	/// </summary>
	public Sm4CryptoMode CryptoMode { get; set; } = Sm4CryptoMode.CBC;

	#region 加密

	/// <summary>
	/// 根据指定的加密模式进行加密
	/// </summary>
	/// <param name="data">明文数据</param>
	/// <param name="key">秘钥</param>
	/// <returns>加密后的字节数组</returns>
	public byte[] Encrypt(string data, string key)
	{
		return this.CryptoMode == Sm4CryptoMode.CBC ? EncryptCBC(data, key) : EncryptECB(data, key);
	}

	/// <summary>
	/// 使用 ECB 模式进行加密
	/// </summary>
	/// <param name="data">明文数据</param>
	/// <param name="key">秘钥</param>
	/// <returns>加密后的字节数组</returns>
	public byte[] EncryptECB(string data, string key)
	{
		Sm4Context ctx = new Sm4Context { IsPadding = true };

		byte[] keyBytes = this.HexString ? Hex.Decode(key) : Encoding.Default.GetBytes(key);

		Sm4 sm4 = new Sm4();
		sm4.SetKeyEnc(ctx, keyBytes);
		return sm4.Sm4CryptEcb(ctx, Encoding.Default.GetBytes(data));
	}

	/// <summary>
	/// 使用 CBC 模式进行加密
	/// </summary>
	/// <param name="data">明文数据</param>
	/// <param name="key">秘钥</param>
	/// <returns>加密后的字节数组</returns>
	public byte[] EncryptCBC(string data, string key)
	{
		return EncryptCBC(data, key, this.Iv);
	}

	/// <summary>
	/// 使用 CBC 模式进行加密，支持自定义 IV
	/// </summary>
	/// <param name="data">明文数据</param>
	/// <param name="key">秘钥</param>
	/// <param name="iv">初始化向量（IV）</param>
	/// <returns>加密后的字节数组</returns>
	public byte[] EncryptCBC(string data, string key, string iv)
	{
		Sm4Context ctx = new Sm4Context { IsPadding = true };

		byte[] keyBytes = this.HexString ? Hex.Decode(key) : Encoding.Default.GetBytes(key);
		byte[] ivBytes = Encoding.Default.GetBytes(iv);

		Sm4 sm4 = new Sm4();
		sm4.SetKeyEnc(ctx, keyBytes);
		return sm4.Sm4CryptCbc(ctx, ivBytes, Encoding.Default.GetBytes(data));
	}

	#endregion 加密

	#region 解密

	/// <summary>
	/// 根据指定的加密模式进行解密
	/// </summary>
	/// <param name="data">密文数据</param>
	/// <param name="key">秘钥</param>
	/// <returns>解密后的字节数组</returns>
	public byte[] Decrypt(string data, string key)
	{
		return this.CryptoMode == Sm4CryptoMode.CBC ? DecryptCBC(data, key) : DecryptECB(data, key);
	}

	/// <summary>
	/// 使用 ECB 模式进行解密
	/// </summary>
	/// <param name="data">密文数据</param>
	/// <param name="key">秘钥</param>
	/// <returns>解密后的字节数组</returns>
	public byte[] DecryptECB(string data, string key)
	{
		Sm4Context ctx = new Sm4Context { IsPadding = true, Mode = 0 };

		byte[] keyBytes = this.HexString ? Hex.Decode(key) : Encoding.Default.GetBytes(key);

		Sm4 sm4 = new Sm4();
		sm4.Sm4SetKeyDec(ctx, keyBytes);
		return sm4.Sm4CryptEcb(ctx, Hex.Decode(data));
	}

	/// <summary>
	/// 使用 CBC 模式进行解密
	/// </summary>
	/// <param name="data">密文数据</param>
	/// <param name="key">秘钥</param>
	/// <returns>解密后的字节数组</returns>
	public byte[] DecryptCBC(string data, string key)
	{
		return DecryptCBC(data, key, this.Iv);
	}

	/// <summary>
	/// 使用 CBC 模式进行解密，支持自定义 IV
	/// </summary>
	/// <param name="data">密文数据</param>
	/// <param name="key">秘钥</param>
	/// <param name="iv">初始化向量（IV）</param>
	/// <returns>解密后的字节数组</returns>
	public byte[] DecryptCBC(string data, string key, string iv)
	{
		Sm4Context ctx = new Sm4Context { IsPadding = true, Mode = 0 };

		byte[] keyBytes = this.HexString ? Hex.Decode(key) : Encoding.Default.GetBytes(key);
		byte[] ivBytes = Encoding.Default.GetBytes(iv);

		Sm4 sm4 = new Sm4();
		sm4.Sm4SetKeyDec(ctx, keyBytes);
		return sm4.Sm4CryptCbc(ctx, ivBytes, Hex.Decode(data));
	}

	#endregion 解密

	/// <summary>
	/// 加密模式
	/// </summary>
	public enum Sm4CryptoMode
	{
		/// <summary>
		/// ECB（电码本模式）
		/// </summary>
		[Description("ECB模式")] ECB = 0,

		/// <summary>
		/// CBC（密码分组链接模式）
		/// </summary>
		[Description("CBC模式")] CBC = 1
	}
}
